// utils/fileHash.js
import CryptoJS from 'crypto-js';

class FileHashCalculator {

	// 计算文件hash - 完整版本
	static calculateFileHash(fileInfo) {
		// console.log('开始计算文件hash，文件信息:', fileInfo);
		return new Promise((resolve, reject) => {
			// #ifdef H5
			// console.log('H5环境计算hash');
			this.calculateFileHashH5(fileInfo).then(resolve).catch(reject);
			// #endif

			// #ifndef H5
			// console.log('小程序环境计算hash');
			this.calculateFileHashMiniProgram(fileInfo).then(resolve).catch(reject);
			// #endif
		});
	}
	// H5环境计算文件hash - 完整版本
	static calculateFileHashH5(fileInfo) {
		return new Promise((resolve, reject) => {
			fetch(fileInfo.url)
				.then(response => response.blob())
				.then(blob => {
					const fileSize = blob.size;
					// console.log(`H5环境，文件大小: ${fileSize} bytes`);

					// 小文件直接计算完整hash
					if (fileSize <= 2 * 1024 * 1024) { // 2MB以下
						// console.log('H5使用完整hash计算');
						this.calculateFullHashH5(blob).then(resolve).catch(reject);
						return;
					}

					// 大文件使用取样策略
					// console.log('H5使用取样hash计算');
					this.calculateSampledHashH5(blob, fileSize).then(resolve).catch(reject);
				})
				.catch(reject);
		});
	}
	// H5完整计算文件hash
	static calculateFullHashH5(blob) {
		return new Promise((resolve, reject) => {
			const fileReader = new FileReader();
			fileReader.onload = function(e) {
				try {
					const arrayBuffer = e.target.result;
					// console.log('完整读取文件大小:', arrayBuffer.byteLength, 'bytes');
					const wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(arrayBuffer));
					const hash = CryptoJS.MD5(wordArray).toString();
					// console.log('完整hash结果:', hash);
					resolve(hash);
				} catch (error) {
					// console.error('完整hash计算失败:', error);
					reject(error);
				}
			};
			fileReader.onerror = (error) => {
				console.error('文件读取失败:', error);
				reject(error);
			};
			fileReader.readAsArrayBuffer(blob);
		});
	}
	// H5取样计算文件hash
	static calculateSampledHashH5(blob, fileSize) {
		return new Promise((resolve, reject) => {
			// console.log(`H5取样计算，文件大小: ${fileSize} bytes`);

			const md5 = CryptoJS.algo.MD5.create();
			const sampleSize = 512 * 1024; // 每个取样块512KB
			const totalSamples = 4; // 总共取样4个位置

			// 计算取样位置
			const samplePositions = this.calculateSamplePositions(fileSize, sampleSize, totalSamples);
			// console.log('H5取样位置:', samplePositions.map((pos, i) => `样本${i}: ${pos}-${Math.min(pos + sampleSize, fileSize)}`));

			let completedSamples = 0;
			const fileReader = new FileReader();
			let currentSample = 0;

			fileReader.onload = function(e) {
				try {
					const arrayBuffer = e.target.result;
					// console.log(`样本 ${currentSample} 读取完成，大小: ${arrayBuffer.byteLength} bytes`);

					const wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(arrayBuffer));
					md5.update(wordArray);
					completedSamples++;

					if (completedSamples < totalSamples) {
						loadNextSample();
					} else {
						// 添加文件大小信息到哈希计算
						const sizeString = fileSize.toString();
						const sizeWordArray = CryptoJS.enc.Utf8.parse(sizeString);
						md5.update(sizeWordArray);
						// console.log('H5添加文件大小到hash:', fileSize);

						const hash = md5.finalize().toString();
						console.log('H5最终取样hash:', hash);
						resolve(hash);
					}
				} catch (error) {
					console.error('H5处理样本时出错:', error);
					reject(error);
				}
			};

			fileReader.onerror = (error) => {
				console.error('H5读取文件出错:', error);
				reject(error);
			};

			const loadNextSample = () => {
				if (currentSample >= totalSamples) return;

				const position = samplePositions[currentSample];
				const start = Math.max(0, position);
				const end = Math.min(start + sampleSize, fileSize);

				// console.log(`H5读取样本 ${currentSample}: ${start}-${end}`);
				fileReader.readAsArrayBuffer(blob.slice(start, end));
				currentSample++;
			};

			loadNextSample();
		});
	}
	// 小程序环境计算文件hash - 完整版本
	static calculateFileHashMiniProgram(fileInfo) {
		return new Promise((resolve, reject) => {
			const fileSize = fileInfo.size;
			// console.log(`小程序环境，文件大小: ${fileSize} bytes`);

			// 小文件直接计算完整hash
			if (fileSize <= 2 * 1024 * 1024) { // 2MB以下
				// console.log('小程序使用完整hash计算');
				this.calculateFullHashMiniProgram(fileInfo).then(resolve).catch(reject);
				return;
			}

			// 大文件使用取样策略
			// console.log('小程序使用取样hash计算');
			this.calculateSampledHashMiniProgram(fileInfo, fileSize).then(resolve).catch(reject);
		});
	}
	// 小程序完整计算文件hash
	static calculateFullHashMiniProgram(fileInfo) {
		return new Promise((resolve, reject) => {
			const fs = uni.getFileSystemManager();
			fs.readFile({
				filePath: fileInfo.url,
				success: (res) => {
					try {
						// console.log('小程序完整读取文件大小:', res.data.byteLength, 'bytes');
						const wordArray = CryptoJS.lib.WordArray.create(res.data);
						const hash = CryptoJS.MD5(wordArray).toString();
						// console.log('小程序完整hash结果:', hash);
						resolve(hash);
					} catch (error) {
						console.error('小程序完整hash计算失败:', error);
						reject(error);
					}
				},
				fail: (err) => {
					console.error('小程序读取文件失败:', err);
					// 生成降级hash
					const fallbackHash = CryptoJS.MD5(fileInfo.name + fileInfo.size + Date
						.now()).toString();
					console.log('小程序使用降级hash:', fallbackHash);
					resolve(fallbackHash);
				}
			});
		});
	}
	// 小程序取样计算文件hash
	static calculateSampledHashMiniProgram(fileInfo, fileSize) {
		return new Promise((resolve, reject) => {
			// console.log(`小程序取样计算，文件大小: ${fileSize} bytes`);

			const fs = uni.getFileSystemManager();
			const sampleSize = 512 * 1024; // 每个取样块512KB
			const totalSamples = 4; // 总共取样4个位置

			// 计算取样位置
			const samplePositions = this.calculateSamplePositions(fileSize, sampleSize, totalSamples);
			// console.log('小程序取样位置:', samplePositions.map((pos, i) =>`样本${i}: ${pos}-${Math.min(pos + sampleSize, fileSize)}`));

			let completedSamples = 0;
			let md5 = CryptoJS.algo.MD5.create();
			let currentSample = 0;

			const readNextSample = () => {
				if (currentSample >= totalSamples) {
					// 添加文件大小信息到哈希计算
					const sizeString = fileSize.toString();
					const sizeWordArray = CryptoJS.enc.Utf8.parse(sizeString);
					md5.update(sizeWordArray);
					// console.log('小程序添加文件大小到hash:', fileSize);

					const hash = md5.finalize().toString();
					// console.log('小程序最终取样hash:', hash);
					resolve(hash);
					return;
				}

				const position = samplePositions[currentSample];
				const start = Math.max(0, position);
				const length = Math.min(sampleSize, fileSize - start);

				// console.log(`小程序读取样本 ${currentSample}: ${start}-${start + length}`);

				fs.readFile({
					filePath: fileInfo.url,
					position: start,
					length: length,
					success: (res) => {
						try {
							// console.log(
							// 	`小程序样本 ${currentSample} 读取完成，大小: ${res.data.byteLength} bytes`
							// );
							const wordArray = CryptoJS.lib.WordArray.create(res.data);
							md5.update(wordArray);
							completedSamples++;
							currentSample++;
							readNextSample();
						} catch (error) {
							console.error('小程序处理样本时出错:', error);
							reject(error);
						}
					},
					fail: (err) => {
						console.error(`小程序读取样本 ${currentSample} 失败:`, err);
						// 如果某个取样失败，继续下一个
						currentSample++;
						readNextSample();
					}
				});
			};

			readNextSample();
		});
	}
	// 统一的取样位置计算函数
	static calculateSamplePositions(fileSize, sampleSize, totalSamples) {
		const positions = [];

		// 第一个样本：开头
		positions.push(0);

		// 中间样本
		for (let i = 1; i < totalSamples - 1; i++) {
			const position = Math.floor((i / (totalSamples - 1)) * fileSize) - sampleSize / 2;
			positions.push(Math.max(0, position));
		}

		// 最后一个样本：结尾
		positions.push(Math.max(0, fileSize - sampleSize));

		// 确保位置不会重叠且不会超出文件范围
		for (let i = 1; i < positions.length; i++) {
			if (positions[i] < positions[i - 1] + sampleSize) {
				positions[i] = positions[i - 1] + sampleSize;
			}
			if (positions[i] + sampleSize > fileSize) {
				positions[i] = Math.max(0, fileSize - sampleSize);
			}
		}

		return positions;
	}
}

export default FileHashCalculator;